﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml;
using System.Xml.Linq;
using System.IO;

namespace Killboard.Models
{
    public class KillMailXmlParser
    {
        public KillMailXmlParser()
        {
        }

        public KMParseResults ParseXML(string XML)
        {
            StringReader sr = new StringReader(XML);
            XDocument doc = XDocument.Load(sr);
            return ParseXML(doc);
        }

        public KMParseResults ParseXML(XmlDocument XML)
        {
            return ParseXML(XML.InnerXml);
        }

        public KMParseResults ParseXML(XDocument doc)
        {
            KillRepository repo = new KillRepository();
            KMParseResults results = new KMParseResults();

            

            //get each individual kill.
            var kills = from c in doc.Elements("eveapi").Elements("result").Elements("rowset").Elements("row")
                        where (string)c.Parent.Attribute("name") == "kills"
                        select c;


            foreach (var kill in kills)
            {
                bool cleanKill = true;

                string killDate = (string)kill.Attribute("killTime");

                DateTime killUTC = DateTime.ParseExact(killDate, "yyyy-MM-dd HH:mm:ss", null);
                ekbKill existingKill = repo.GetKillByDate(killUTC);


                try
                {
                    ekbKill eKill = new ekbKill();

                    //      <row killID="10140387" solarSystemID="30002216" killTime="2009-12-06 14:47:00" moonID="0">

                    eKill.KillTimeUTC = killUTC;
                    eKill.eveKillNumber = (int)kill.Attribute("killID");
                    eKill.SolarSystemID = (int)kill.Attribute("solarSystemID");

                    var victim = from k in kill.Elements("victim")
                                 select k;

                    if (victim.Count() != 1)
                    {
                        throw new KillParser.BadKillException(string.Format("Bad victim count on kill: {0}", killDate));
                    }

                    //<victim characterID="1160134871" characterName="Nocturnal Spirit" corporationID="148119825" corporationName="Takahashi Syndicate" allianceID="0" allianceName="" factionID="0" factionName="" damageTaken="5613" shipTypeID="22544" />

                    string victimName = (string)victim.First().Attribute("characterName");

                    if (victimName.Length == 0)
                    {
                        throw new KillParser.BadKillException(string.Format("Kill {0} had a blank pilot name.", killDate));
                    }

                    eKill.VictimID = repo.FindOrAddPilot(victimName, (int)victim.First().Attribute("characterID"));
                    eKill.VictimCorpID = repo.FindOrAddCorp((string)victim.First().Attribute("corporationName"));

                    string allianceName = "NONE";
                    if ((int)victim.First().Attribute("allianceID") != 0)
                    {
                        allianceName = (string)victim.First().Attribute("allianceName");
                    }

                    eKill.VictimAllianceID = repo.FindOrAddAlliance(allianceName);

                    eKill.VictimShipID = (short?)victim.First().Attribute("shipTypeID");
                    eKill.DamageTaken = (int)victim.First().Attribute("damageTaken");


                    var involved = from k in kill.Elements("rowset").Elements("row")
                                   where (string)k.Parent.Attribute("name") == "attackers"
                                   select k;

                    foreach (var killer in involved)
                    {
                        //<row characterID="342639044" characterName="Presidente Gallente" corporationID="829578575" corporationName="Order of the Griffin" allianceID="0" allianceName="" factionID="0" factionName="" securityStatus="-0.731701631574405" damageDone="6820" finalBlow="1" weaponTypeID="2446" shipTypeID="12005" />

                        //Note:  if characterID is zero, then this is an NPC.

                        if ((int)killer.Attribute("characterID") == 0)
                        {
                            ekbInvolved npc = new ekbInvolved();
                            npc.CorpID = (int)killer.Attribute("corporationID");
                            npc.npcID = (short)killer.Attribute("shipTypeID");
                            npc.PilotID = null;
                            npc.ShipID = (short?)killer.Attribute("shipTypeID");
                            npc.WeaponID = (short?)killer.Attribute("shipTypeID");
                            npc.LaidFinalBlow = ((int)killer.Attribute("finalBlow") == 1 ? true : false);
                            npc.isNPC = true;
                            npc.DamageDone = (int?)killer.Attribute("damageDone"); 
                            eKill.ekbInvolveds.Add(npc);

                            continue;
                        }
                            

                        ekbInvolved attacker = new ekbInvolved();
                        attacker.PilotID = repo.FindOrAddPilot((string)killer.Attribute("characterName"), (int)killer.Attribute("characterID"));
                        attacker.CorpID = repo.FindOrAddCorp((string)killer.Attribute("corporationName"));
                        
                        allianceName = "NONE";
                        if ((int)killer.Attribute("allianceID") != 0)
                        {
                            allianceName = (string)killer.Attribute("allianceName");
                        }

                        attacker.AllianceID = repo.FindOrAddAlliance(allianceName);
                        attacker.secStatus = (decimal?)killer.Attribute("securityStatus");
                        attacker.ShipID = (short?)killer.Attribute("shipTypeID");
                        attacker.WeaponID = (short?)killer.Attribute("weaponTypeID");
                        attacker.DamageDone = (int?)killer.Attribute("damageDone");

                        int wasFinalBlow = (int)killer.Attribute("finalBlow");

                        attacker.LaidFinalBlow = (wasFinalBlow == 1 ? true : false);

                        eKill.ekbInvolveds.Add(attacker);
                    }


                    //now item drops
                    var items = from k in kill.Elements("rowset").Elements("row")
                                   where (string)k.Parent.Attribute("name") == "items"
                                   select k;


                    foreach (var item in items)
                    {
                        //<row typeID="252" flag="5" qtyDropped="0" qtyDestroyed="1" />
                        
                        int qtyDropped = (int)item.Attribute("qtyDropped");
                        int qtyDestroyed = (int)item.Attribute("qtyDestroyed");
                        short itemID = (short)item.Attribute("typeID");

                        bool cargo = false;
                        
                        int flag = (int)item.Attribute("flag");
                        
                        //5 = cargo, 87 = drone bay (from table invFlags)
                        if (flag == 5 || flag == 87)
                        {
                            cargo = true;
                        }


                        
                        ekbItemDrop dropped = null;
                        ekbItemDrop destroyed = null;

                        if (qtyDropped > 0)
                        {
                            dropped = new ekbItemDrop();
                            dropped.ItemID = itemID;
                            dropped.itemQty = qtyDropped;
                            dropped.DestroyedFlag = false;
                            dropped.CargoFlag = cargo;
                        }

                        if(qtyDestroyed > 0)
                        {
                            destroyed = new ekbItemDrop();
                            destroyed.ItemID = itemID;
                            destroyed.itemQty = qtyDestroyed;
                            destroyed.DestroyedFlag = true;
                            destroyed.CargoFlag = cargo;
                        }

                        if (dropped != null)
                        {
                            eKill.ekbItemDrops.Add(dropped);
                        }

                        if (destroyed != null)
                        {
                            eKill.ekbItemDrops.Add(destroyed);
                        }


                    }

                    //Check existing kill.  If exists, skip.
                    if (existingKill != null)
                    {
                        results.KMsSkipped.Add(killUTC);
                        continue;
                    }


                    KillRepository insert = new KillRepository();

                    insert.AddKill(eKill);

                    results.KMsAdded.Add(killUTC);
   
                }
                catch (Exception e)
                {
                    results.KMsInError.Add(killUTC);
                    results.errors.Add(killUTC, e);
                    continue;
                }
            }

            return results;
        }
    }


    public class KMParseResults
    {
        public List<DateTime> KMsAdded = new List<DateTime>();
        public List<DateTime> KMsSkipped = new List<DateTime>();
        public List<DateTime> KMsInError = new List<DateTime>();

        public Dictionary<DateTime, Exception> errors = new Dictionary<DateTime, Exception>();
    }
}
